﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;

using dotUML.Core.Common;
using dotUML.Core.Parser.SequenceDiagram;
using dotUML.System;

namespace dotUML.Core.Parser
{
    /// <summary>
    /// XmlParser class implements the top level parsing: stereotypes and diagram nodes.
    /// </summary>
    class XmlParser : XmlParserBase
    {
        ISequenceDiagramXmlParser _sequenceDiagramParser;
        IXmlParser _stereotypesParser;

        /// <summary>
        /// Sequence diagram parser.
        /// </summary>
        public ISequenceDiagramXmlParser SequenceDiagramParser
        {
            get { return _sequenceDiagramParser; }
            set { _sequenceDiagramParser = value; }
        }
        /// <summary>
        /// Stereotypes parser.
        /// </summary>
        public IXmlParser StereotypesParser
        {
            get { return _stereotypesParser; }
            set { _stereotypesParser = value; }
        }

        /// <summary>
        /// Initialize parser fields.
        /// </summary>
        private void InitializeParser()
        {
            SequenceDiagramParser = (ISequenceDiagramXmlParser)InstrumentationManager.InstrumentObject(new SequenceDiagramXmlParser(), EInstrumentationType.Logging);
            StereotypesParser = (IXmlParser)InstrumentationManager.InstrumentObject(new StereotypesXmlParser(), EInstrumentationType.Logging);
        }

        /// <summary>
        /// Parameterless class constructor.
        /// </summary>
        public XmlParser()
            : base()
        {
            InitializeParser();
        }

        /// <summary>
        /// Class constructor.
        /// </summary>
        /// <param name="Errors">Parsing errors list.</param>
        /// <param name="Model">dotUML model</param>
        public XmlParser(List<IError> Errors)
            : base(Errors)
        {
            InitializeParser();
        }

        /// <summary>
        /// Parse an xml-file and create a dotUML model based on the file's content.
        /// </summary>
        /// <param name="Xml">Xml diagram description.</param>
        /// <param name="Model">dotUML model.</param>
        /// <returns>True if parsing is succsessful, otherwise false.</returns>
        public override bool Parse(string Xml, IModel Model)
        {
            this.Model = Model;

            XDocument document = XDocument.Parse(Xml);
            XElement stereotypes = document.Element(Constants.Model.ModelNode).Element(Constants.Stereotype.StereotypesNode);
            StereotypesParser.Parse(stereotypes.ToString(), Model);

            XElement diagrams = document.Element(Constants.Model.ModelNode).Element(Constants.Model.DiagramsNode);

            IEnumerable<XElement> sequenceDiagrams = diagrams.Elements(Constants.SequenceDiagram.SequenceDiagramNode);
            foreach(XElement sequenceDiagram in sequenceDiagrams)
            {
                SequenceDiagramParser.Parse(sequenceDiagram.ToString(), Model);
            }


            return base.Parse(Xml, Model);
        }

        /// <summary>
        /// Validate an xml-file against an xml-schema.
        /// </summary>
        /// <param name="Xml">String representation of an xml-file.</param>
        /// <returns>True if the file is valid, otherwise - false.</returns>
        public override bool ValidateXml(string Xml)
        {
            Stream xsdStream;
            XmlReaderSettings settings;
            XmlDocument document;

            // Load xsd from embedded assembly resources.
            try
            {
                xsdStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("dotUML.Core.Parser.dotUMLLanguage.xsd");
            }
            catch (Exception ex)
            {
                throw new ParserException(Resources.XmlParser_ValidateXml_GetEmbeddedXsd, ex);
            }

            // Create xml reader settings and initialize them with an xsd.
            try
            {
                XmlReader xsd = XmlReader.Create(xsdStream);
                settings = new XmlReaderSettings();
                settings.Schemas.Add(null, xsd);
                settings.ValidationType = ValidationType.Schema;
            }
            catch (Exception ex)
            {
                throw new ParserException(Resources.XmlParser_ValidateXml_LoadXsd, ex);
            }

            // Load a dotUML xml file and process validation errors.
            try
            {
                TextReader xmlText = new StringReader(Xml);
                XmlReader reader = XmlReader.Create(xmlText, settings);
                document = new XmlDocument();
                document.Load(reader);
            }
            catch (Exception ex)
            {
                NewError(EErrorType.Error, ex.Message);
            }

            return (Errors.Count == 0);
        }
    
    }


}
